<template>
  <div class="upload-container">
    <div
      class="upload-dropzone"
      @dragover.prevent
      @drop.prevent="handleDrop"
      @click="triggerFileInput"
    >
      <input
        type="file"
        ref="fileInput"
        style="display: none"
        accept=".zip"
        @change="handleFileSelect"
      />
      <div class="upload-icon">
        <i class="fas fa-cloud-upload-alt"></i>
      </div>
      <div class="upload-text">
        <p>点击或拖拽ZIP文件到此处上传</p>
        <p class="upload-hint">（仅支持ZIP文件）</p>
      </div>
    </div>

    <div v-if="currentFile" class="upload-progress">
      <div class="file-info">
        <span>{{ currentFile.name }}</span>
        <span>{{ formatFileSize(currentFile.size) }}</span>
      </div>
      <div class="progress-bar">
        <div
          class="progress-inner"
          :style="{ width: `${uploadProgress}%` }"
        ></div>
      </div>
      <div class="progress-text">
        <span>{{ uploadStatus }}</span>
        <span>{{ uploadProgress }}%</span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, defineEmits } from "vue";
import { useWebSocket } from "@/hooks/socketClient";
import SparkMD5 from "spark-md5";
import axios from "axios";

// 文件上传配置
const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB分片大小
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL + "/api";

// 组件状态
const fileInput = ref(null);
const currentFile = ref(null);
const uploadProgress = ref(0);
const uploadStatus = ref("");
const currentTaskId = ref(null);

// WebSocket连接
// 假设 useWebSocket hook 返回一个 disconnect 函数用于清理
const { connected, onMessage, disconnect } = useWebSocket(
  "upload",
  import.meta.env.VITE_API_BASE_WS_URL
);

// 监听WebSocket消息
onMessage("progress", (data) => {
  if (data.identifier === currentFile.value?.identifier) {
    uploadProgress.value = data.progress;
    uploadStatus.value = "正在上传...";
  }
});

onMessage("merge_progress", (data) => {
  if (data.taskId === currentTaskId.value) {
    uploadProgress.value = data.progress;
    uploadStatus.value = "正在合并文件...";
  }
});

onMessage("merge_completed", (data) => {
  if (data.taskId === currentTaskId.value) {
    uploadProgress.value = 100;
    uploadStatus.value = "上传完成";
    // 触发上传完成事件
    emit("uploadComplete", {
      url: data.url,
      fileName: currentFile.value.name,
    });
    // 重置状态
    setTimeout(() => {
      currentFile.value = null;
      uploadProgress.value = 0;
      uploadStatus.value = "";
      currentTaskId.value = null;
    }, 1000);
  }else{
    emit("uploadComplete", {
      taskId: data.taskId,
      fileName: data.originalName,
    });
  }
});

onMessage("error", (data) => {
  if (data.taskId === currentTaskId.value) {
    uploadStatus.value = "上传失败";
    // 重置状态
    setTimeout(() => {
      currentFile.value = null;
      uploadProgress.value = 0;
      uploadStatus.value = "";
      currentTaskId.value = null;
    }, 1000);
  }
});

// 计算文件哈希
async function calculateHash(file) {
  return new Promise((resolve) => {
    const spark = new SparkMD5.ArrayBuffer();
    const reader = new FileReader();
    const size = file.size;
    const offset = 2 * 1024 * 1024; // 取前2MB计算哈希

    reader.onload = (e) => {
      spark.append(e.target.result);
      const hash = spark.end();
      resolve(hash);
    };

    const slice = file.slice(0, offset);
    reader.readAsArrayBuffer(slice);
  });
}

// 创建文件分片
function createFileChunks(file) {
  const chunks = [];
  let cur = 0;
  while (cur < file.size) {
    chunks.push({
      index: chunks.length,
      file: file.slice(cur, cur + CHUNK_SIZE),
    });
    cur += CHUNK_SIZE;
  }
  return chunks;
}

// 上传单个分片
async function uploadChunk(chunk, identifier, totalChunks, fileInfo) {
  const formData = new FormData();

  formData.append("chunkIndex", chunk.index);
  formData.append("identifier", identifier);
  formData.append("totalChunks", totalChunks);
  formData.append("fileName", fileInfo.fileName);
  formData.append("fileSize", fileInfo.fileSize);
  formData.append("mimeType", fileInfo.mimeType);
  formData.append("file", chunk.file);

  await axios.post(`${API_BASE_URL}/upload/chunk`, formData);
}

// 请求合并分片
async function mergeRequest(identifier, fileName, totalChunks) {
  const response = await axios.post(`${API_BASE_URL}/upload/merge`, {
    identifier,
    fileName,
    totalChunks,
  });
  return response.data;
}

// 检查文件是否已上传（秒传）
async function checkFileExists(fileHash) {
  try {
    const response = await axios.get(`${API_BASE_URL}/upload/check`, {
      params: { fileHash },
    });
    return response.data;
  } catch (error) {
    return { exists: false };
  }
}

// 获取已上传的分片
async function getUploadedChunks(identifier) {
  try {
    const response = await axios.get(`${API_BASE_URL}/upload/chunk-status`, {
      params: { identifier },
    });
    return response.data.uploaded || [];
  } catch (error) {
    return [];
  }
}

// 处理文件上传
async function handleUpload(file) {
  // 首先检查WebSocket连接状态
  if (!connected.value) {
    alert("与服务器的实时连接已断开，请刷新页面或检查网络后重试。");
    if (fileInput.value) {
      fileInput.value.value = null; // 清空文件选择，以便用户可以重新选择
    }
    // 如果 currentFile 指向当前尝试上传的文件，也清除它
    if (
      currentFile.value &&
      currentFile.value.name === file.name &&
      currentFile.value.size === file.size
    ) {
      currentFile.value = null;
    }
    uploadStatus.value = "连接失败"; // 更新状态以反映问题
    uploadProgress.value = 0; // 重置进度
    return;
  }

  if (
    !file ||
    (file.type !== "application/zip" &&
      file.type !== "application/x-zip-compressed")
  ) {
    alert("请选择ZIP文件上传");
    return;
  }

  if (file.size > 1024 * 1024 * 1024 * 1024) {
    alert("文件大小不能超过1TB");
    return;
  }

  currentFile.value = file;
  uploadProgress.value = 0; // 确保在开始处理文件时重置进度
  uploadStatus.value = "准备上传..."; // 设置初始状态

  try {
    uploadStatus.value = "正在计算文件哈希..."; // 更新为更具体的状态
    // 计算文件哈希
    const fileHash = await calculateHash(file);
    const identifier = `${fileHash}-${file.name}`;
    currentFile.value.identifier = identifier;

    // 检查秒传
    const checkResult = await checkFileExists(fileHash);
    if (checkResult.exists) {
      uploadProgress.value = 100;
      uploadStatus.value = "文件秒传成功";
      emit("uploadComplete", {
        url: checkResult.url,
        fileName: checkResult.originalName,
      });
      // 重置状态
      setTimeout(() => {
        currentFile.value = null;
        uploadProgress.value = 0;
        uploadStatus.value = "";
        currentTaskId.value = null;
      }, 2000);
      return;
    }

    // 获取已上传的分片
    const uploadedChunks = await getUploadedChunks(identifier);

    // 创建分片
    const chunks = createFileChunks(file);
    const fileInfo = {
      fileName: file.name,
      fileSize: file.size,
      mimeType: file.type,
    };

    // 上传分片
    uploadStatus.value = "正在上传...";
    for (const chunk of chunks) {
      if (!uploadedChunks.includes(chunk.index)) {
        await uploadChunk(chunk, identifier, chunks.length, fileInfo);
      }
    }

    // 请求合并分片
    uploadStatus.value = "正在合并文件...";
    const mergeResult = await mergeRequest(
      identifier,
      file.name,
      chunks.length
    );
    currentTaskId.value = mergeResult.taskId;
  } catch (error) {
    console.error("上传失败:", error);
    uploadStatus.value = "上传失败";
    setTimeout(() => {
      currentFile.value = null;
      uploadProgress.value = 0;
      uploadStatus.value = "";
    }, 2000);
  }
}

// 触发文件选择
function triggerFileInput() {
  fileInput.value.click();
}

// 处理文件选择
function handleFileSelect(event) {
  const file = event.target.files[0];
  if (file) {
    handleUpload(file);
  }
  event.target.value = null; // 重置input，允许选择相同文件
}

// 处理文件拖放
function handleDrop(event) {
  const file = event.dataTransfer.files[0];
  if (file) {
    handleUpload(file);
  }
}

// 格式化文件大小
function formatFileSize(bytes) {
  if (bytes === 0) return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
}

// 定义事件
const emit = defineEmits(["uploadComplete"]);

onMounted(() => {
  // 如果需要，可以在此添加初始化逻辑
});

onUnmounted(() => {
  // 组件卸载时尝试断开WebSocket连接
  if (typeof disconnect === "function") {
    disconnect();
  }
});
</script>

<style scoped>
.upload-container {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
}

.upload-dropzone {
  border: 2px dashed #e0e0e0;
  border-radius: 8px;
  padding: 40px;
  text-align: center;
  cursor: pointer;
  transition: all 0.3s ease;
}

.upload-dropzone:hover {
  border-color: #1976d2;
  background-color: rgba(25, 118, 210, 0.04);
}

.upload-icon {
  font-size: 48px;
  color: #757575;
  margin-bottom: 16px;
}

.upload-text {
  color: #757575;
}

.upload-hint {
  font-size: 14px;
  margin-top: 8px;
  color: #9e9e9e;
}

.upload-progress {
  margin-top: 24px;
  padding: 16px;
  background-color: #f5f5f5;
  border-radius: 8px;
}

.file-info {
  display: flex;
  justify-content: space-between;
  margin-bottom: 8px;
  font-size: 14px;
}

.progress-bar {
  height: 4px;
  background-color: #e0e0e0;
  border-radius: 2px;
  overflow: hidden;
  margin: 8px 0;
}

.progress-inner {
  height: 100%;
  background-color: #1976d2;
  transition: width 0.3s ease;
}

.progress-text {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  color: #757575;
}
</style>
